package logic

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"video-api/internal/consts"
	"video-api/internal/dao"
	"video-api/internal/model/entity"
	"video-api/utility/queue"
	"video-api/utility/redis_client"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/hibiken/asynq"
	"github.com/redis/go-redis/v9"
)

type AddDramaHistoryHandler struct{}

func NewAddDramaHistoryHandler() *AddDramaHistoryHandler {
	return &AddDramaHistoryHandler{}
}

func (a *AddDramaHistoryHandler) ProcessTask(ctx context.Context, task *asynq.Task) error {
	fmt.Println("Processing AddDramaHistory task================================")
	var data queue.AddShortDramaHistory
	err := json.Unmarshal(task.Payload(), &data)
	if err != nil {
		g.Log().Error(ctx, "add drama history:json unmarshal err:", err)
		return err
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 查询现有记录
		sdrModel := dao.ShortDramaRecord.Ctx(ctx)
		sdrResult, err := sdrModel.Where("user_id = ?", data.UserId).
			Where("short_drama_id = ?", data.ShortDramaId).One()
		if err != nil && errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, "add drama history:query history record error:", err)
			return fmt.Errorf("query history record error: %w", err)
		}
		// 获取短剧信息
		sdModel := dao.ShortDrama.Ctx(ctx)
		shortDramaInfo, err := sdModel.Where("id = ?", data.ShortDramaId).
			Fields("id as short_drama_id,short_name,description,cover_img,tags").One()
		if err != nil {
			g.Log().Error(ctx, "add drama history:query short drama error:", err)
			return fmt.Errorf("query short drama error: %w", err)
		}
		if len(shortDramaInfo) == 0 {
			g.Log().Error(ctx, "add drama history:short drama not found:", data.ShortDramaId)
			return fmt.Errorf("short drama not found: %d", data.ShortDramaId)
		}
		// 获取标签信息
		tags := make([]entity.Tags, 0)
		sdtModel := dao.ShortDramaTags.Ctx(ctx).Fields("t.*")
		err = sdtModel.LeftJoin("tags t", "t.id = short_drama_tags.tag_id").
			//Where("short_drama_tags.tag_id = ?", shortDramaInfo["tags"]).
			Where("short_drama_id = ?", data.ShortDramaId).Scan(&tags)
		if err != nil && err != sql.ErrNoRows {
			g.Log().Error(ctx, "add drama history:query tags error:", err)
			return fmt.Errorf("query tags error: %w", err)
		}
		// 获取剧集信息
		sdeModel := dao.ShortDramaEpisodes.Ctx(ctx)
		shortDramaEpisodesInfo, err := sdeModel.Where("id = ?", data.EpisodesId).
			Fields("order").One()
		if err != nil {
			g.Log().Error(ctx, "add drama history:query episodes error:", err)
			return fmt.Errorf("query episodes error: %w", err)
		}
		if len(shortDramaEpisodesInfo) == 0 {
			g.Log().Error(ctx, "add drama history:query episodes error:", err)
			return fmt.Errorf("episode not found: %d", data.EpisodesId)
		}
		var historyId int64
		tagsList := make([]string, 0)
		if len(tags) > 0 {
			for _, tag := range tags {
				tagsList = append(tagsList, tag.Title)
			}
		}
		//tageByte, _ := json.Marshal(tagsList)
		insertOrUpdateData := g.Map{
			"user_id":        data.UserId,
			"short_drama_id": data.ShortDramaId,
			"episodes_id":    data.EpisodesId,
			"play_at":        data.PlayTime,
			"category":       consts.HISTORY,
			"order":          shortDramaEpisodesInfo["order"],
			"short_name":     shortDramaInfo["short_name"],
			"description":    shortDramaInfo["description"],
			"cover_img":      shortDramaInfo["cover_img"],
			"tags":           strings.Join(tagsList, ","),
		}
		if len(sdrResult) == 0 {
			insertResult, err := sdrModel.Data(insertOrUpdateData).Insert()
			if err != nil {
				g.Log().Error(ctx, "add drama history:insert history record error:", err)
				return fmt.Errorf("insert history record error: %w", err)
			}
			historyId, err = insertResult.LastInsertId()
			if err != nil {
				g.Log().Error(ctx, "add drama history:get last insert id error:", err)
				return fmt.Errorf("get last insert id error: %w", err)
			}
		} else {
			historyId = sdrResult["id"].Int64()
			_, err := sdrModel.Data(insertOrUpdateData).
				Where("user_id = ?", data.UserId).
				Where("short_drama_id = ?", data.ShortDramaId).
				Update()
			if err != nil {
				g.Log().Error(ctx, "add drama history:update history record error:", err)
				return fmt.Errorf("update history record error: %w", err)
			}
		}

		// 更新 Redis 缓存
		if err := updateRedisHistory(ctx, insertOrUpdateData, historyId, sdrResult, data); err != nil {
			g.Log().Error(ctx, "add drama history:update redis error:", err)
			return fmt.Errorf("update redis error: %w", err)
		}
		return nil
	})
	if err != nil {
		g.Log().Error(ctx, "add drama history:doHistoryRecord transaction failed:", err)
		return err
	}
	return nil
}

func updateRedisHistory(ctx context.Context, record g.Map, historyId int64, sdrResult gdb.Record, data queue.AddShortDramaHistory) error {
	historyData := g.Map{
		"id":           historyId,
		"shortDramaId": record["short_drama_id"],
		"shortName":    record["short_name"],
		"description":  record["description"],
		"coverImg":     record["cover_img"],
		"tags":         record["tags"],
		"order":        record["order"],
		"episodesId":   record["episodes_id"],
	}
	userId, ok := record["user_id"].(int64)
	if !ok {
		g.Log().Error(ctx, "add drama history:updateRedisHistory userId convert to int error:", fmt.Errorf("updateRedisHistory user id not int"))
		return fmt.Errorf("updateRedisHistory user id not int")
	}
	key := consts.SHORT_DRAMA_HISTORY + ":" + strconv.Itoa(int(userId))
	if len(sdrResult) > 0 {
		oldHistoryData := g.Map{
			"id":           sdrResult["id"],
			"shortDramaId": sdrResult["short_drama_id"],
			"shortName":    sdrResult["short_name"],
			"description":  sdrResult["description"],
			"coverImg":     sdrResult["cover_img"],
			"tags":         sdrResult["tags"],
			"order":        sdrResult["order"],
			"episodesId":   sdrResult["episodes_id"],
		}
		m, err := json.Marshal(oldHistoryData)
		if err != nil {
			g.Log().Error(ctx, "add drama history:updateRedisHistory marshal old history data error:", err)
			return fmt.Errorf("marshal old history data error: %w", err)
		}
		if err := redis_client.RedisClient.ZRem(ctx, key, m).Err(); err != nil {
			g.Log().Error(ctx, "add drama history:updateRedisHistory remove old history from redis error:", err)
			return fmt.Errorf("remove old history from redis error: %w", err)
		}
	}
	m, err := json.Marshal(historyData)
	if err != nil {
		g.Log().Error(ctx, "add drama history:updateRedisHistory marshal new history data error:", err)
		return fmt.Errorf("marshal new history data error: %w", err)
	}
	member := redis.Z{
		Score:  float64(data.PlayTime.Unix()),
		Member: m,
	}
	if err := redis_client.RedisClient.ZAdd(ctx, key, member).Err(); err != nil {
		g.Log().Error(ctx, "add drama history:updateRedisHistory add new history to redis error:", err)
		return fmt.Errorf("add new history to redis error: %w", err)
	}
	result2, err := redis_client.RedisClient.ZCard(ctx, key).Result()
	if err != nil {
		g.Log().Error(ctx, "add drama history:updateRedisHistory get redis zcard error:", err)
		return fmt.Errorf("get redis zcard error: %w", err)
	}
	if result2 > int64(100) {
		if _, err := redis_client.RedisClient.ZRemRangeByRank(ctx, key, 0, result2-int64(100)-1).Result(); err != nil {
			g.Log().Error(ctx, "add drama history:updateRedisHistory trim redis history error:", err)
			return fmt.Errorf("trim redis history error: %w", err)
		}
	}
	return nil
}
